package fons.model.query.fun;

import java.util.Collection;
import java.util.List;

import com.google.common.collect.Lists;

public class ReferenceFun extends ListFun {
	private final String name;
	private final List<Function> arguments = Lists.newLinkedList();
	private final ReferenceFun parent;
	
	private Scope scope;
	
	public ReferenceFun (String name) {	
		this.name = name;
		this.parent = null;
	}
	
	private ReferenceFun (ReferenceFun parent, String name, List<Function> arguments, Function argument) {
		this.name = name;
		this.parent = parent;
		this.arguments.addAll(arguments);
		this.arguments.add(argument);
	}
	
	public Function setScope(Scope scope) {
		this.scope = scope;
		return this;
	}
	
	public Scope getScope() {
		if (scope == null) {
			return parent == null ? new Scope() : parent.getScope();
		}
		return scope;
	}
	
	@Override
	public Function apply(Function other) {
		if (!getScope().contains(name)) {
			return new ReferenceFun(parent == null ? this : parent, name, arguments, other);
		}
		
		return getScope().get(name).apply(other);
	}

	@Override
	public Function apply() {		
		if (!getScope().contains(name)) return this;		
		
		Function reference = getScope().get(name);
		for (Function arg: arguments) {
			Function last = arg;
			while (arg instanceof ReferenceFun) {
				arg = ((ReferenceFun) arg).apply();
			}
			if (arg == null) arg = last;
			reference = reference.apply(arg);
		}

		Function fun = reference.apply();
		while (fun instanceof ReferenceFun) {
			fun = ((ReferenceFun) fun).apply();
		}
		
		return fun;
	}
	

	@Override
	public List<? extends Function> get() {
		if (!getScope().contains(name))
			return Lists.newArrayList(this);
			//return defaultList.get(); 
			//throw new IllegalStateException("Reference doesn't refer to any list");
		return ((ListFun) getScope().get(name)).get();
	}
	
	@Override
	public Collection<? extends Number> getList(Class<? extends Number> clazz) {
		if (!getScope().contains(name)) /*return defaultList.getList(clazz);*/ throw new IllegalStateException("Reference doesn't refer to any list");
		return ((ListFun) getScope().get(name)).getList(clazz);
	}	
	
	@Override
	public String toString() {
		return "reference to \"" + name + "\"";
	}

	public String getName() {
		return this.name;
	}
	
	public boolean isBound() {
		return getScope().contains(name);
	}
}
